// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.http.util;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import com.eva.framework.utils.LoggerFactory;
import com.sun.mail.util.MailSSLSocketFactory;

/**
 * 要发出的邮件元数据对象（一封邮件一个实例）.
 * <p>
 * 20170921日Jack Jiang 增加了SSL的支持。
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 */
public class OutgoingMail
{
	//定义发件人、收件人、SMTP服务器、用户名、密码、主题、内容等
	private String displayName;
	private String to;
	private String from;
	private String smtpServer;
	/** SMTP服务器端口（默认是不支持SSL的普通SMTP端口25） */
	private String smtpPort="25";
	private String username;
	private String password;
	private String subject;
	private String content;
	/** 服务器是否要身份认证（默认true） */
	private boolean ifAuth=true;
	/** 服务器是否需要SSL（默认false） */
	private boolean supportSSL=false;

	/** 用于保存发送附件的文件名的集合 */
	private Vector file = new Vector(); 
	/** 是否支持BUBUG信息输出 */
	private boolean canDebug = false;

	public OutgoingMail()
	{
	}

	/**
	 * 初始化SMTP服务器地址、发送者E-mail地址、用户名、密码、接收者、主题、内容。
	 * 
	 * @param smtpServer 发件SMTP主机域名
	 * @param from 发件人邮件地址
	 * @param displayNamev 发件人姓名（非必须）
	 * @param username 发件人邮箱用户名（就是@符号前面那串）
	 * @param password 发件人邮箱明文密码
	 * @param to 收件人邮件地址
	 * @param subject 邮件主题
	 * @param content 邮件内容
	 * @param smtpPort SMTP服务端端口，如果本参数为空，则自动使用默认的25端口
	 * @param supportSSL 是否需要使用SSL支持，现在越来越多的邮件服务商不提供非SSL方式了，如需支持请设为true
	 */
	public OutgoingMail(String smtpServer, String from, String displayName
			, String username, String password, String to, String subject
			, String content, String smtpPort, boolean supportSSL)
	{
		this.smtpServer = smtpServer;
		this.from = from;
		this.displayName = displayName;
		this.ifAuth = true;
		this.username = username;
		this.password = password;
		this.to = to;
		this.subject = subject;
		this.content = content;
		
		this.smtpPort = smtpPort;
		this.supportSSL = supportSSL;
	}

//	/**
//	 * 初始化SMTP服务器地址、发送者E-mail地址、接收者、主题、内容
//	 */
//	public OutgoingMail(String smtpServer, String from, String displayName,
//			String to, String subject, String content)
//	{
//		this.smtpServer = smtpServer;
//		this.from = from;
//		this.displayName = displayName;
//		this.ifAuth = false;
//		this.to = to;
//		this.subject = subject;
//		this.content = content;
//	}

	/**
	 * 发送邮件
	 */
	public HashMap send() throws Exception
	{
		HashMap map = new HashMap();
		map.put("state", "success");
		String message = "邮件发送成功！";
		Session session = null;
		
		Properties props = System.getProperties();
		// SMTP服务器
		props.put("mail.smtp.host", smtpServer);
		// 端口
		props.setProperty("mail.smtp.port", (smtpPort == null || smtpPort.length() == 0)?"25":smtpPort);
		
		// 如果需要支持SSL
		if(this.supportSSL)
		{
			// 使用SSL企业邮箱必需： 开启SSL安全协议
			MailSSLSocketFactory sf = null;
			try {
				sf = new MailSSLSocketFactory();
				sf.setTrustAllHosts(true);
			} catch (GeneralSecurityException e1) {
				e1.printStackTrace();
			}
			props.put("mail.smtp.ssl.enable", "true");
			props.put("mail.smtp.ssl.socketFactory", sf);
		}
		
		// 发件SMTP服务器需要发件人的身份认证
		if (ifAuth)
		{
			props.put("mail.smtp.auth", "true");
			SmtpAuthImpl smtpAuth = new SmtpAuthImpl(username, password);
			session = Session.getDefaultInstance(props, smtpAuth);
		}
		else
		{
			props.put("mail.smtp.auth", "false");
			session = Session.getDefaultInstance(props, null);
		}
		session.setDebug(canDebug);
		Transport trans = null;
		try
		{
			Message msg = new MimeMessage(session);
			try
			{
				Address from_address = new InternetAddress(from, displayName);
				msg.setFrom(from_address);
			}
			catch (java.io.UnsupportedEncodingException e)
			{
				e.printStackTrace();
				throw e;
			}
			InternetAddress[] address = { new InternetAddress(to) };
			msg.setRecipients(Message.RecipientType.TO, address);
			msg.setSubject(subject);//.setSubject(subject);
			Multipart mp = new MimeMultipart();
			MimeBodyPart mbp = new MimeBodyPart();
			mbp.setContent(content.toString(), "text/html;charset=UTF-8");
			mp.addBodyPart(mbp);
			if (!file.isEmpty())
			{//有附件
				Enumeration efile = file.elements();
				while (efile.hasMoreElements())
				{
					mbp = new MimeBodyPart();

					String filePath = "",filename = "";
					Object of = efile.nextElement();

					//文件名是否使用与路径不一样的文件名（别名）
					boolean useDiffName = false;
					//本对象的出现就是为了使用别名
					if(of instanceof Attachfile)
					{
						filePath = ((Attachfile)of).getFilePath();
						filename = ((Attachfile)of).getFileName();
						useDiffName = true;
					}
					else if(of instanceof String)
					{
						//不使用别名
						filePath = of.toString();
					}
					else
					{
//						System.out.println("未知附件文件信息对象！"+of);
						LoggerFactory.getLog().debug("【邮件数据准备中】smtp="+smtpServer+",port="+smtpPort
								+",uname="+username+",psw="+password+"(SSL? "+this.supportSSL+"), message="
								+"未知附件文件信息对象！"+of);
					}

					//得到数据源
					FileDataSource fds = new FileDataSource(filePath);
					//得到附件本身并至入BodyPart
					mbp.setDataHandler(new DataHandler(fds));
					//得到文件名同样至入BodyPart(记得将文件名MimeUtility.encodeText，否则就等着乱码吧，
					//其实就是转换成BASE64编码再加上首尾的个别附加字符而已）
					mbp.setFileName(MimeUtility.encodeText(useDiffName?filename:fds.getName()));
					mp.addBodyPart(mbp);
				}
				file.removeAllElements();
			}
			msg.setContent(mp); //Multipart加入到信件
			msg.setSentDate(new Date()); //设置信件头的发送日期
			//发送信件
			msg.saveChanges();
			trans = session.getTransport("smtp");
			LoggerFactory.getLog().debug("【正在发送邮件】smtp="+smtpServer+",port="+smtpPort
					+",uname="+username+",psw="+password+"(SSL? "+this.supportSSL+")");
			trans.connect(smtpServer, username, password);
			trans.sendMessage(msg, msg.getAllRecipients());
			trans.close();

		}
		catch (AuthenticationFailedException e)
		{
			map.put("state", "failed");
			message = "邮件发送失败！错误原因：\n" + "身份验证错误!";
//			e.printStackTrace();
			LoggerFactory.getLog().debug("【邮件发送失败】smtp="+smtpServer+",port="+smtpPort
					+",uname="+username+",psw="+password+"(SSL? "+this.supportSSL+"), message="+message, e);
			throw e;
		}
		catch (MessagingException e)
		{
			message = "邮件发送失败！错误原因：\n" + e.getMessage();
			map.put("state", "failed");
//			e.printStackTrace();
			LoggerFactory.getLog().debug("【邮件发送失败】smtp="+smtpServer+",port="+smtpPort
					+",uname="+username+",psw="+password+"(SSL? "+this.supportSSL+"), message="+message, e);
			Exception ex = null;
			if ((ex = e.getNextException()) != null)
			{
//				System.out.println(ex.toString());
//				ex.printStackTrace();
				LoggerFactory.getLog().debug(ex.toString(), ex);
			}

			throw e;
		}
		//System.out.println("\n提示信息:"+message);
		map.put("message", message);
		return map;
	}

	/**
	 * 设置SMTP服务器地址
	 */
	public void setSmtpServer(String smtpServer)
	{
		this.smtpServer = smtpServer;
	}

	/**
	 * 设置发件人的地址
	 */
	public void setFrom(String from)
	{
		this.from = from;
	}

	/**
	 * 设置显示的名称
	 */
	public void setDisplayName(String displayName)
	{
		this.displayName = displayName;
	}

	/**
	 * 设置服务器是否需要身份认证
	 */
	public void setIfAuth(boolean ifAuth)
	{
		this.ifAuth = ifAuth;
	}

	/**
	 * 设置E-mail用户名
	 */
	public void setUserName(String username)
	{
		this.username = username;
	}

	/**
	 * 设置E-mail密码
	 */
	public void setPassword(String password)
	{
		this.password = password;
	}

	/**
	 * 设置接收者
	 */
	public void setTo(String to)
	{
		this.to = to;
	}

	/**
	 * 设置主题
	 */
	public void setSubject(String subject)
	{
		this.subject = subject;
	}

	/**
	 * 设置主体内容
	 */
	public void setContent(String content)
	{
		this.content = content;
	}

	/**
	 * 该方法用于收集附件名
	 */
	public void addAttachfile(String fname)
	{
		file.addElement(fname);
	}

	/**
	 * 该方法用于收集附件
	 */
	public void addAttachfile(Attachfile fname)
	{
		file.addElement(fname);
	}

	public boolean isCanDebug()
	{
		return canDebug;
	}

	public void setCanDebug(boolean canDebug)
	{
		this.canDebug = canDebug;
	}

	/**
	 * 存放附件文件基信息的内部类.
	 */
	public static class Attachfile
	{
		private String filePath,fileName;
		public Attachfile(String filePath,String fileName)
		{
			this.filePath = filePath;
			this.fileName = fileName;
		}
		public String getFilePath()
		{
			return filePath;
		}
		public void setFilePath(String filePath)
		{
			this.filePath = filePath;
		}
		public String getFileName()
		{
			return fileName;
		}
		public void setFileName(String fileName)
		{
			this.fileName = fileName;
		}
	}

	/**
	 * SMTP认证对象.
	 */
	public class SmtpAuthImpl extends Authenticator
	{
		private String username, password;

		public SmtpAuthImpl(String username, String password)
		{
			this.username = username;
			this.password = password;
		}

		protected PasswordAuthentication getPasswordAuthentication()
		{
			return new PasswordAuthentication(username, password);
		}
	}

	public static void main(String args[]) throws Exception
	{
//		// 发送网易邮件：使用非SSL方式，测试成功！
////		OutgoingMail om = new OutgoingMail("smtp.163.com", "jb2011@163.com", "MissU Master",
////				"jb2011","????????????????",
//////				"js4713@163.com",
////				"413980957@qq.com",
////				"邀请你使用MissU", "邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU."
////				, "25", false);
//
//		// 发送QQ邮件：使用SSL方式，测试成功！
////		OutgoingMail om = new OutgoingMail("smtp.qq.com"
////				, "249751626@qq.com"
////				, "XXMissU Master"
////				, "249751626"
////				,"????????????????????",
////				"413980957@qq.com",
////				"XX邀请你使用MissU"
////				, "XX邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU."
////				, "465", true);
//		
//		// 发送新浪邮件：使用非SSL方式，测试成功！
//		OutgoingMail om = new OutgoingMail("smtp.sina.com", "hellojackjiang@sina.com", "SSAAMissU Master",
//				"hellojackjiang","??????????",
////				"js4713@163.com",
//				"413980957@qq.com",
//				"SSAA邀请你使用MissU"
//				, "SSAA邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU邀请你使用MissU."
//				, "25", false);
//		
////		om.addAttachfile("C:\\js4713.txt");
////		om.addAttachfile("C:\\Data1.cab");
////		om.addAttachfile("d:\\复件1147.bmp");
////		om.addAttachfile("d:\\最令人感动的谎言（伟大的母爱）.doc");
////		om.addAttachfile(new Attachfile("d:\\复件jad.exe","jad_别名.exe"));
//
//		HashMap hm = om.send();
//		if(hm.get("message")!=null)//发送成功了
//		{
//			System.out.println(hm.get("message"));
//		}
//		else
//		{
//			System.out.println(hm.get("state"));
//		}
	}
}
